package main

import (
	"errors"
	"server/src/common"

	mgo "gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"

	"server/src/protocol808"
)

// DBOperation 数据库操作类
type DBOperation struct {
	session *mgo.Session
	dbName  string
	cfg     *Config
}

const (
	CollectionRawDatas       string = "rawdatas"       //原始数据表
	CollectionRawDatasLast   string = "rawdataslast"   //最新的原始数据表
	CollectionReceivers      string = "receivers"      //接收器基本信息表
	CollectionSensors        string = "sensors"        //传感器基本信息表
	CollectionTires          string = "tires"          //轮胎基本信息表
	CollectionPositions      string = "positions"      //位置信息表
	CollectionPositionsLast  string = "positionslast"  //最新位置信息表
	CollectionTrackDates     string = "trackdates"     //轨迹日期表
	CollectionLastStatuses   string = "laststatuses"   //最新状态表
	CollectionRelations      string = "relations"      //关联表
	CollectionVehicles       string = "vehicles"       //汽车表
	CollectionTimeStatistics string = "timestatistics" //工作时长统计表

	//CollectionMileageStatistics string = "mileagestatistics" //里程统计表
	//CollectionIpBlackLists     string = "ipblacklists"    //IP黑名单表
	//CollectionsIllegalAccesses string = "illegalaccesses" //非法请求纪录表
)

const (
	RawDataStatNew   int = 0 //新纪录
	RawDataStatRead  int = 1 //已被读取，但未处理
	RawDataStatDone  int = 2 //处理完成
	RawDataStatError int = 3 //异常
)

// 每次读入的最大纪录数
const MaxReadCount int = 1000

// GPS纪录类别
const GPSDataType string = "0200"

// NewDBOperation 创建新的数据库操作类
func NewDBOperation(cfg *Config) (*DBOperation, error) {
	zLog("数据库连接::", cfg.DBUrl, " 开始")
	session, err := mgo.Dial(cfg.DBUrl)
	if err != nil {
		zLog(nil, "数据库连接失败::", err.Error())
		return nil, err
	}
	if cfg.DBUser != "" && cfg.DBPwd != "" {
		err = session.Login(&mgo.Credential{
			Username: cfg.DBUser,
			Password: cfg.DBPwd,
		})
		if err != nil {
			zLog("数据库登录失败::", err.Error())
			return nil, err
		}
	}
	session.SetPoolLimit(10) //设置连接缓冲池的最大值

	dbo := DBOperation{session: session, dbName: cfg.DBName, cfg: cfg}

	return &dbo, nil
}

// Close the database
func (p *DBOperation) Close() {
	p.session.Close()
}

// 原始数据结构
type RawDataT struct {
	ID   bson.ObjectId `bson:"_id"`
	Imei string        `bson:"imei"`
	T    int64         `bson:"t"`
	Data string        `bson:"data"`
}

// 读取最新的1000条GPS原始数据
func (p *DBOperation) GetNewRawDatas() ([]RawDataT, error) {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionRawDatasLast)
	var rds []RawDataT
	sortField := "t"
	if p.cfg.SortField == common.OrderDesc {
		sortField = "-t"
	}

	err := c.Find(bson.M{"dt": GPSDataType, "stat": RawDataStatNew}).
		Sort(sortField).Limit(MaxReadCount).All(&rds)

	return rds, err
}

func (p *DBOperation) UpdateNewRawDatas() {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionRawDatasLast)
	_, _ = c.UpdateAll(bson.M{"dt": GPSDataType, "stat": RawDataStatRead}, bson.M{"$set": bson.M{"stat": RawDataStatNew}})
}

// 删除最新原始数据记录
func (p *DBOperation) RemoveLastRawData(id bson.ObjectId) {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionRawDatasLast)
	c.Remove(bson.M{"_id": id})
}

// 更新最新原始数据记录
func (p *DBOperation) UpdateLastRawData(id bson.ObjectId) {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionRawDatasLast)
	c.Update(bson.M{"_id": id}, bson.M{"$set": bson.M{"stat": RawDataStatRead}})
}

// 位置信息表结构
type PositionsT struct {
	RawId        bson.ObjectId `bson:"rawid"`  //原始数据表中的纪录标识
	Rid          string        `bson:"rid"`    //接收器编号
	T            int64         `bson:"t"`      //纪录的时间戳
	Latitude     float32       `bson:"lat"`    //纬度
	Longitude    float32       `bson:"lng"`    //经度
	GCJLatitude  float32       `bson:"latg"`   //GCJ-02的纬度
	GCJLongitude float32       `bson:"lngg"`   //GCJ-02的经度
	BDLatitude   float32       `bson:"latf"`   //纠偏后的纬度
	BDLongitude  float32       `bson:"lngf"`   //纠偏后的经度
	Height       int           `bson:"height"` //高度，单位：米
	Speed        float32       `bson:"speed"`  //速度，单位：公里/小时
	Direction    int           `bson:"dir"`    //方向，正北为0，顺时针0-359
	Date         int           `bson:"date"`   //格式：YYMMDD
	Time         int           `bson:"time"`   //格式：HHNNSS

	AlarmMark        protocol808.AlarmMark_t              `bson:"alarm"` //报警标志
	TerminalStatus   protocol808.TerminalStatus_t         `bson:"stat"`  //状态定义
	PositionAddition protocol808.TerminalPositionAddition `bson:"add"`   //位置附加信息
	//protocol808.TerminalPosition               //位置信息
}

// 保存位置数据
func (p *DBOperation) SavePositions(pos *PositionsT) error {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionPositions)
	var res interface{}
	err := c.Find(bson.M{"rawid": pos.RawId}).Select(bson.M{"_id": 1}).Limit(1).One(&res)
	if err == nil {
		return errors.New("本条原始数据已处理过，不保存")
	}

	//保存到最新位置信息中
	cLast := s.DB(p.dbName).C(CollectionPositionsLast)
	cLast.Insert(pos)

	return c.Insert(pos)
}

// 保存某传感器有轨迹的日期数据
func (p *DBOperation) SaveGpsDay(rid string, date int) {
	s := p.session.Copy()
	defer s.Close()

	year := date / 10000
	month := (date - year*10000) / 100
	day := date % 100

	c := s.DB(p.dbName).C(CollectionTrackDates)
	c.Upsert(bson.M{"rid": rid, "year": year, "month": month}, bson.M{"$addToSet": bson.M{"days": day}})
}

// 最新定位信息
type LastLocationT struct {
	LastTime     int64   `bson:"t"`
	Latitude     float32 `bson:"lat"`
	Longitude    float32 `bson:"lng"`
	LatitudeBD   float32 `bson:"latb"`
	LongitudeBD  float32 `bson:"lngb"`
	LatitudeGCJ  float32 `bson:"latg"`
	LongitudeGCJ float32 `bson:"lngg"`
	Speed        float32 `bson:"speed"`
	Height       int     `bson:"height"`
	Direction    int     `bson:"dir"`
}

// 最新运动状态
type LastMotionT struct {
	LastTime int64 `bson:"t"`
	Motion   int   `bson:"motion"`
}

type LastStatusT struct {
	Rid string `bson:"rid"`
	//Type              int           `bson:"type"`
	Vol               float32       `bson:"vol"`
	GSMSignalStrength int           `bson:"gsm"`
	GPSSatelliteCount int           `bson:"gpssc"`
	PowerSource       int           `bson:"ps"`
	ModuleStatus      int           `bson:"ms"`
	LastLocation      LastLocationT `bson:"ll"`
	LastMotion        LastMotionT   `bson:"lm"`
}

// 保存GPS数据到最新状态表
func (p *DBOperation) SaveNewGPSData(rid string, date int, time int, lat float32, lng float32,
	latb float32, lngb float32, latg float32, lngg float32, speed float32, height int, dir int, vol float32, acc int,
	gsmSignalStrength int, gpsSatelliteCount int, powerSource int, moduleStatus int) {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionLastStatuses)
	var ls LastStatusT

	err := c.Find(bson.M{"rid": rid}).Select(bson.M{"_id": 0, "rid": 1, "vol": 1, "type": 1, "ll": 1, "lm": 1,
		"gpssc": 1, "gsm": 1, "ps": 1, "ms": 1}).One(&ls)

	if err == nil {
		//有纪录，则需要对比时间
		needUpdate := false
		bsonUpdate := make(bson.M)

		//判断是否需要更新电量
		if ls.Vol != vol {
			bsonUpdate["vol"] = vol
			needUpdate = true
		}

		if ls.GPSSatelliteCount != gpsSatelliteCount {
			bsonUpdate["gpssc"] = gpsSatelliteCount
			needUpdate = true
		}

		if ls.GSMSignalStrength != gsmSignalStrength {
			bsonUpdate["gsm"] = gsmSignalStrength
			needUpdate = true
		}

		if ls.PowerSource != powerSource {
			bsonUpdate["ps"] = powerSource
			needUpdate = true
		}

		if ls.ModuleStatus != moduleStatus {
			bsonUpdate["ms"] = moduleStatus
			needUpdate = true
		}

		//判断是否需要更新最新定位
		var newTime int64 = int64(date*1000000) + int64(time)
		if lat != 0.0 && lng != 0.0 {
			if newTime >= ls.LastLocation.LastTime {
				//更新最新状态表
				ls.LastLocation.LastTime = newTime
				ls.LastLocation.Latitude = lat
				ls.LastLocation.Longitude = lng
				ls.LastLocation.LatitudeBD = latb
				ls.LastLocation.LongitudeBD = lngb
				ls.LastLocation.LatitudeGCJ = latg
				ls.LastLocation.LongitudeGCJ = lngg
				ls.LastLocation.Speed = speed
				ls.LastLocation.Height = height
				ls.LastLocation.Direction = dir

				bsonUpdate["ll"] = ls.LastLocation
				bsonUpdate["location"] = bson.M{"type": "Point", "coordinates": []float32{lng, lat}}

				needUpdate = true
			}
		}

		//判断是否需要更新最新运动状态
		if newTime > ls.LastMotion.LastTime {
			if ls.LastMotion.Motion == 1 && acc == 1 {
				//原来是运动状态，本次仍是运动状态，则累计工作时间
				dur := intTimeToDuration(int(ls.LastMotion.LastTime), int(newTime))
				if dur > 0 {
					p.saveWorkTime(rid, date, dur)
				}
			}
			ls.LastMotion.Motion = acc
			ls.LastMotion.LastTime = newTime

			bsonUpdate["lm"] = ls.LastMotion
			needUpdate = true
		}

		if needUpdate {
			c.Update(bson.M{"rid": rid}, bson.M{"$set": bsonUpdate})
		}
	}
}

// 累计工作时长
func (p *DBOperation) saveWorkTime(rid string, date int, worktime int) {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionTimeStatistics)
	y := date / 10000
	m := date % 10000 / 100
	d := date % 100

	err := c.Find(bson.M{"rid": rid, "date": date}).Select(bson.M{"_id": 1}).One(nil)
	if err == nil {
		c.Update(bson.M{"rid": rid, "date": date}, bson.M{"$inc": bson.M{"wt": worktime}})
	} else {
		c.Insert(bson.M{"rid": rid, "date": date, "y": y, "m": m, "d": d, "wt": worktime})
	}
}

// 查询接收器的关联关系
type RelationT struct {
	CardId   string `bson:"cid"`
	SensorId string `bson:"sid"`
	TireId   string `bson:"tid"`
}

// 获取接收器状态
func (p *DBOperation) GetRelation(receiverid string) ([]RelationT, error) {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionRelations)
	var r []RelationT

	err := c.Find(bson.M{"rid": receiverid}).Select(bson.M{"sid": 1, "tid": 1, "cid": 1, "_id": 0}).All(&r)
	if err != nil {
		return nil, err
	} else {
		return r, nil
	}
}

// 获取接收器状态
func (p *DBOperation) ReceiverStatus(rid string) int {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionReceivers)
	var res map[string]int

	err := c.Find(bson.M{"rid": rid}).Select(bson.M{"_id": 0, "status": 1}).One(&res)
	if err != nil {
		return 0
	}
	return res["status"]
}
